/* @flow */

import { hasOwn } from "shared/util";
import { warn, hasSymbol } from "../util/index";
import { defineReactive, toggleObserving } from "../observer/index";

export function initProvide(vm: Component) {
  const provide = vm.$options.provide;
  if (provide) {
    vm._provided = typeof provide === "function" ? provide.call(vm) : provide;
  }
}

export function initInjections(vm: Component) {
  /* 返回一个经过处理的inject  */
  const result = resolveInject(vm.$options.inject, vm);
  if (result) {
    /* 控制属性是否为响应式 */
    toggleObserving(false);
    Object.keys(result).forEach((key) => {
      /* 只在 开发环境报错 */
      if (process.env.NODE_ENV !== "production") {
        /* 将 inject代理到Vm的属性 */
        defineReactive(vm, key, result[key], () => {
          /* 当在子组件里直接改变 inject 的警告 */
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
              `overwritten whenever the provided component re-renders. ` +
              `injection being mutated: "${key}"`,
            vm
          );
        });
      } else {
        defineReactive(vm, key, result[key]);
      }
    });
    toggleObserving(true);
  }
}
/* 对inject中的值进行匹配返回 */
export function resolveInject(inject: any, vm: Component): ?Object {
  /*   hahha: {from: "hahha"}
        testProvider: {from: "testProvider"}
         */
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    const result = Object.create(null);
    const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject);

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      /*  */
      if (key === "__ob__") continue;
      /* 获取 对应的inject properties */
      const provideKey = inject[key].from;
      let source = vm;
      /* 从当前组件一直往上找直到 匹配 */
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey];
          break;
        }
        source = source.$parent;
      }
      if (!source) {
        /* 当前inject是否存在 default值 */
        if ("default" in inject[key]) {
          const provideDefault = inject[key].default;
          result[key] =
            typeof provideDefault === "function"
              ? provideDefault.call(vm)
              : provideDefault;
          /* 否则报错 */
          /* Injection "hahha" not found */
        } else if (process.env.NODE_ENV !== "production") {
          warn(`Injection "${key}" not found`, vm);
        }
      }
    }
    return result;
  }
}
